package com.yanyeori.framework.core.util.excel;

import com.yanyeori.framework.core.exception.BusinessException;
import com.yanyeori.framework.core.interfaces.InterfaceFunction;
import com.yanyeori.framework.core.util.FieldUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.util.Assert;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

/**
 * Excel读工具
 *
 * @author chenkuan
 * @since 2021/5/25
 */
public class ExcelReader extends ExcelUtil {
    /**
     * sheet表
     */
    private final Sheet sheet;

    /**
     * 配置
     */
    private final ExcelConfig config;

    /**
     * 构造函数，初始化excel数据
     */
    private ExcelReader(ExcelConfig excelConfig) throws IOException {
        this.config = excelConfig;
        Assert.hasLength(config.filePath, "The file path cannot be empty");
        Workbook workbook = getWorkbook(config.filePath);
        if (config.sheetName != null && config.sheetName.length() > 0) {
            sheet = workbook.getSheet(config.sheetName);
        } else {
            sheet = workbook.getSheetAt(config.sheetIndex);
        }
    }

    /**
     * build ExcelReader.ExcelConfig
     */
    public static ExcelReader.ExcelConfig builder() {
        return new ExcelReader.ExcelConfig();
    }

    /**
     * 根据excel路径获取excel表Workbook
     *
     * @param filePath excel路径
     */
    private Workbook getWorkbook(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            throw new BusinessException("The file path [{}] does not exist", filePath);
        }
        FileInputStream fileInputStream = new FileInputStream(filePath);
        Workbook workbook;
        String fileName = file.getName();
        if (fileName.endsWith(".xlsx")) {
            workbook = new XSSFWorkbook(fileInputStream);
        } else if (fileName.endsWith(".xls")) {
            workbook = new HSSFWorkbook(fileInputStream);
        } else {
            throw new BusinessException("Unsupported file type: {}", fileName);
        }
        return workbook;
    }

    /**
     * 读取指定行和列索引单元格的数据
     *
     * @param rowIndex    行索引（1开始）
     * @param columnIndex 列索引（1开始）
     * @param tClass      返回bean字段类型
     */
    public <T> T readByIndex(int rowIndex, int columnIndex, Class<T> tClass) {
        //excel索引为0开始，输入值-1处理
        rowIndex--;
        columnIndex--;
        Row row = sheet.getRow(rowIndex);
        if (row == null) {
            return null;
        }
        Cell cell = row.getCell(columnIndex);
        return transferFromCell(cell, null, tClass);
    }

    /**
     * 读取excel数据到java bean
     *
     * @param supplier bean
     * @param startRow 开始行索引，从1开始
     * @param endRow   结束行索引，从1开始
     * @return List<bean>
     */
    public <T> List<T> read(Supplier<T> supplier, int startRow, Integer endRow) throws ReflectiveOperationException {
        //excel索引为0开始，输入值-1处理
        startRow--;
        if (endRow != null) {
            endRow--;
        }
        List<T> list = new ArrayList<>();
        //处理字段index对应位置
        indexFieldMap(config.indexFieldMap, supplier.get().getClass());
        //最后一行的行号
        int lastRowNum = sheet.getLastRowNum();
        int endRowIndex = endRow == null ? lastRowNum : endRow < lastRowNum ? endRow : lastRowNum;
        for (int i = startRow; i <= endRowIndex; i++) {
            Row row = sheet.getRow(i);
            if (row == null) {
                continue;
            }
            RowData<T> rowData = readRow(row, supplier, config.indexFieldMap);
            if (config.skipAllEmptyColumnRow && rowData.isAllColumnEmpty()) {
                continue;
            }
            list.add(rowData.getData());
        }
        return list;
    }

    /**
     * 配置类
     */
    public static class ExcelConfig {
        /**
         * excel文件路径
         */
        private String filePath;

        /**
         * sheet序号（从0开始）
         */
        private int sheetIndex = 0;

        /**
         * sheet名称
         */
        private String sheetName;

        /**
         * 过滤所有空列行
         */
        private boolean skipAllEmptyColumnRow = true;

        /**
         * excel表列索引与bean字段对应关系，从1开始，默认为bean字段顺序
         */
        private Map<Integer, String> indexFieldMap = new HashMap<>();

        public ExcelReader build() throws IOException {
            return new ExcelReader(this);
        }

        public ExcelConfig setFilePath(String filePath) {
            this.filePath = filePath;
            return this;
        }

        public ExcelConfig setSheetIndex(int sheetIndex) {
            this.sheetIndex = sheetIndex;
            return this;
        }

        public ExcelConfig setSheetName(String sheetName) {
            this.sheetName = sheetName;
            return this;
        }

        public ExcelConfig setSkipAllEmptyColumnRow(boolean skipAllEmptyColumnRow) {
            this.skipAllEmptyColumnRow = skipAllEmptyColumnRow;
            return this;
        }

        public ExcelConfig setIndexField(int index, String field) {
            this.indexFieldMap.put(--index, field);
            return this;
        }

        public <T, R> ExcelConfig setIndexField(int index, InterfaceFunction<T, R> function) throws ReflectiveOperationException {
            this.indexFieldMap.put(--index, FieldUtil.getFieldName(function));
            return this;
        }

        public void clearIndexField() {
            this.indexFieldMap.clear();
        }
    }
}
